<!DOCTYPE HTML>
<html>
	<head>
		<title>Velocity.js Tests</title>
		<link rel="stylesheet" href="qunit-1.14.0.css" type="text/css" media="screen" />
		<script type="text/javascript" src="qunit-1.14.0.js"></script>
		<script type="text/javascript" src="jquery-1.12.4.js"></script>
		<script type="text/javascript" src="when.js"></script>
		<!--<script type="text/javascript" src="zepto.js"></script>-->
		<script type="text/javascript" src="../velocity.js"></script>
		<script type="text/javascript" src="../velocity.ui.js"></script>
		<style>
			#details {
				margin: 0 auto;
				margin-bottom: 1em;

				width: 800px;
				padding: 0;

				line-height: 1.35em;
				list-style: none;
			}
		</style>
	</head>
	<body>
		<ul id="details">	
			<li>
				Unit tests: <i><b>current document</b>.</i>
			</li>
			<li>
				Performance tests: <i>See <b>Performance</b> pane in <a href="../../index.html#performance">docs</a>.</i>
			</li>
			<li>
				Property support tests: <i>Run the Property Support pane's <a href="../../index.html#propertiesTest">auto-cycler</a>.</i>
			</li>
			<li>
				Visual tests: <i>See <a href="../../demo.html"><b>demo</b></a>. Read demo's source for intended behavior.</i>
			</li>
		</ul>
		<div id="qunit"></div>
		<div id="qunit-stage"></div>
		<script>

		// Needed tests:
		// - new stop behvaior
		// - e/p/o shorthands

		/*********************
		   Helper Functions
		*********************/

		/* IE detection: https://gist.github.com/julianshapiro/9098609 */
		var IE = (function() { 
		    if (document.documentMode) {
		        return document.documentMode;
		    } else {
		        for (var i = 7; i > 0; i--) {
		            var div = document.createElement("div");

		            div.innerHTML = "<!" + "--[if IE " + i + "]><span></span><![endif]--" + ">";

		            if (div.getElementsByTagName("span").length) {
		                div = null;

		                return i;
		            }

		            div = null;
		        }
		    }

		    return undefined;
		})();

    	var isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
    	var isAndroid = /Android/i.test(navigator.userAgent);

    	function applyStartValues (element, startValues) {
    		Velocity.Utilities.each(startValues, function(property, startValue) {
    			element.style[property] = startValue;
    		});
    	}

		/*********************
		   IE<=7 Reversion
		*********************/

  		if (IE <= 7 && $.fn.velocity) {
  			alert("Velocity wasn't reverted to jQuery's $.animate() for IE<=7.");
  		}

  		/*******************
  		    jQuery Shim
  		*******************/

  		var $ = (window.jQuery || window.Zepto);

  		Data = function(element) { 
  			return Velocity.Utilities.data(element, "velocity");
  		};

  		if ($ && $.Velocity) {
  			window.Velocity = $.Velocity;
  		}

		/*******************
		   Setup: Targets
		*******************/

		var $qunitStage = document.getElementById("qunit-stage");

		var targetsFragment = document.createDocumentFragment(),
			targetsIndex = 0,
			$targets,
			$targetSet,
			defaultStyles = {
				opacity: 1,
				width: 1,
				height: 1,
				marginBottom: 1,
				colorGreen: 200
			};

		for (var i = 0; i < 200; i++) {
			var div = document.createElement("div");

			div.className = "target";
			div.style.opacity = defaultStyles.opacity;
			div.style.color = "rgb(125, " + defaultStyles.colorGreen + ", 125)";
			div.style.width = defaultStyles.width + "px";
			div.style.height = defaultStyles.height + "px";
			div.style.marginBottom = defaultStyles.marginBottom + "px";
			div.style.textShadow = "0px 0px " + defaultStyles.textShadowBlur + "px red";

			targetsFragment.appendChild(div);
			div = null;
		}

		$qunitStage.appendChild(targetsFragment);
		$targets = $qunitStage.querySelectorAll(".target");

		function getTarget () {
			var newTarget = $targets[targetsIndex++];

			return newTarget;
		}

		/********************
		   Setup: Settings
		********************/

		var	pluginName = "velocity",
			defaultProperties = {
				opacity: defaultStyles.opacity / 2,
				width: defaultStyles.width * 2,
				height: defaultStyles.height * 2,
				colorGreen: defaultStyles.colorGreen / 2
			},
			defaultOptions = { 
				queue: "",
			    duration: 300,
			    easing: "swing",
			    begin: null,
			    complete: null,
				progress: null,
			    display: null,
			    loop: false,
			    delay: false,
			    mobileHA: true,
			    _cacheValues: true
			},
			asyncCheckDuration = defaultOptions.duration / 2,
			completeCheckDuration = defaultOptions.duration * 2;

		Velocity.defaults = defaultOptions;

		/****************
		    Arguments
		****************/

		QUnit.test("Arguments", function(assert) {
			var testComplete = function() { /* Do nothing */ },
				testDuration = 1000,
				testEasing = "easeInSine",
				testOptions = {
					queue: defaultOptions.queue,
					duration: testDuration,
					easing: testEasing,
					begin: null,
					complete: testComplete,
					progress: null,
					display: "block",
					loop: false,
					delay: false,
					mobileHA: false,
			    	_cacheValues: defaultOptions._cacheValues
				};

			/**********************
			   Invalid Arguments
			**********************/

			var $target1 = getTarget();
			/* No arguments: Ensure an error isn't thrown and that the $targeted elements are returned to the chain. */
			Velocity();
			Velocity($target1);
			Velocity($target1, {});
			Velocity($target1, {}, testDuration);
			/* Invalid arguments: Ensure an error isn't thrown. */
			Velocity($target1, "fakeArg1", "fakeArg2");

			/****************
			   Overloading
			****************/

			var $target3 = getTarget();
			Velocity($target3, defaultProperties, testDuration);
			assert.equal(Data($target3, pluginName).opts.duration, testDuration, "Overload variation #2.");

			var $target4 = getTarget();
			Velocity($target4, defaultProperties, testEasing);
			assert.equal(Data($target4, pluginName).opts.easing, testEasing, "Overload variation #3.");

			var $target5 = getTarget();
			Velocity($target5, defaultProperties, testComplete);
			assert.equal(Data($target5, pluginName).opts.complete, testComplete, "Overload variation #4.");

			var $target6 = getTarget();
			Velocity($target6, defaultProperties, testDuration, [ 0.42, 0, 0.58, 1 ]);
			assert.equal(Data($target6, pluginName).opts.duration, testDuration, "Overload variation #5a.");
			assert.equal(Data($target6, pluginName).opts.easing(0.2), 0.0816598562658975, "Overload variation #5b.");

			var $target7 = getTarget();
			Velocity($target7, defaultProperties, testDuration, testComplete);
			assert.equal(Data($target7, pluginName).opts.duration, testDuration, "Overload variation #6a.");
			assert.equal(Data($target7, pluginName).opts.complete, testComplete, "Overload variation #6b.");

			var $target8 = getTarget();
			Velocity($target8, defaultProperties, testDuration, testEasing, testComplete);
			assert.equal(Data($target8, pluginName).opts.duration, testDuration, "Overload variation #7a.");
			assert.equal(Data($target8, pluginName).opts.easing, testEasing, "Overload variation #7b.");
			assert.equal(Data($target8, pluginName).opts.complete, testComplete, "Overload variation #7c.");

			var $target9 = getTarget();
			Velocity($target9, defaultProperties, testOptions);
			assert.deepEqual(Data($target9, pluginName).opts, testOptions, "Overload variation #8: options object.");

			var $target10 = getTarget();
			Velocity({ elements: $target10, properties: defaultProperties, options: testOptions });
			assert.deepEqual(Data($target10, pluginName).opts, testOptions, "Overload variation #9: single object w/ map.");

			var $target11 = getTarget();
			Velocity({ elements: $target11, properties: "fadeOut", options: testOptions });
			assert.strictEqual(Data($target11, pluginName).tweensContainer.opacity.endValue, 0, "Overload variation #9: single object w/ redirect.");

			var $target12 = getTarget();
			Velocity($target12, { opacity: [ 0.75, "spring", 0.25 ]}, testDuration);
			assert.equal(Data($target12, pluginName).tweensContainer.opacity.startValue, 0.25, "Overload variation #10a.");
			assert.equal(Data($target12, pluginName).tweensContainer.opacity.easing, "spring", "Overload variation #10b.");
			assert.equal(Data($target12, pluginName).tweensContainer.opacity.endValue, 0.75, "Overload variation #10c.");

			var $target13 = getTarget();
			Velocity($target13, { opacity: [ 0.75, 0.25 ]}, testDuration);
			assert.equal(Data($target13, pluginName).tweensContainer.opacity.startValue, 0.25, "Overload variation #11a.");
			assert.equal(Data($target13, pluginName).tweensContainer.opacity.endValue, 0.75, "Overload variation #11b.");

			var $target14 = getTarget();
			Velocity($target14, { opacity: [ 0.75, "spring" ]}, testDuration);
			assert.equal(Data($target14, pluginName).tweensContainer.opacity.endValue, 0.75, "Overload variation #12a.");
			assert.equal(Data($target14, pluginName).tweensContainer.opacity.easing, "spring", "Overload variation #12b.");

			var $target15 = getTarget();
			Velocity($target15, defaultProperties, "fast", testEasing);
			assert.equal(Data($target15, pluginName).opts.duration, 200, "Overload variation #13a.");

			var $target16 = getTarget();
			Velocity($target16, defaultProperties, "normal");
			assert.equal(Data($target16, pluginName).opts.duration, 400, "Overload variation #13b.");

			if ($) {
				var $target17 = getTarget();
				$($target17).velocity(defaultProperties, testOptions);
				assert.deepEqual(Data($target17, pluginName).opts, testOptions, "$.fn.: Utility function variation #1: options object.");

				var $target18 = getTarget();
				$($target18).velocity({ properties: defaultProperties, options: testOptions });
				assert.deepEqual(Data($target18, pluginName).opts, testOptions, "$.fn.: Utility function variation #2: single object.");

				var $target19 = getTarget();
				$($target19).velocity(defaultProperties, testDuration, testEasing, testComplete);
				assert.equal(Data($target19, pluginName).opts.duration, testDuration, "$.fn.: Utility function variation #2a.");
				assert.equal(Data($target19, pluginName).opts.easing, testEasing, "$.fn.: Utility function variation #2b.");
				assert.equal(Data($target19, pluginName).opts.complete, testComplete, "$.fn.: Utility function variation #2c.");

				var $target20 = getTarget();
				assert.deepEqual($($target20), $($target20).velocity(defaultProperties, testDuration, testEasing, testComplete).velocity(defaultProperties, testDuration, testEasing, testComplete), "$.fn.: Elements passed back to the call stack.");
			}
		});

		/******************
		    CSS Object
		******************/

		/* Note: We don't bother checking all of the GET/SET-related CSS object's functions, as most are repeatedly tested indirectly via the other tests. */
		QUnit.test("CSS Object", function(assert) {
			var CSS = Velocity.CSS;

			var testHookRoot = "boxShadow",
				testHookRootValue = IE >=9 ? "1px 2px 3px 4px black" : "black 1px 2px 3px 4px",
				testHook = "boxShadowY",
				testHookValueExtracted = "2px",
				testHookValueInject = "10px",
				testHookRootValueInjected = "1px 10px 3px 4px";

			/* Hooks manipulation. */
			assert.equal(CSS.Hooks.getRoot(testHook), testHookRoot, "Hooks.getRoot() returned root.");

			/* Hooks have no effect if they're unsupported (which is the case for our hooks on <=IE8), thus we just ensure that errors aren't thrown. */
			if (IE <=8) {
				CSS.Hooks.extractValue(testHook, testHookRootValue);
				CSS.Hooks.injectValue(testHook, testHookValueInject, testHookRootValue);
			} else {
				assert.equal(CSS.Hooks.extractValue(testHook, testHookRootValue), testHookValueExtracted, "Hooks.extractValue() returned value #1.");
				/* Check for a match anywhere in the string since browser differ in where they inject the color value. */
				assert.equal(CSS.Hooks.injectValue(testHook, testHookValueInject, testHookRootValue).indexOf(testHookRootValueInjected) !== -1, true, "Hooks.extractValue() returned value #2.");
			}

			/* Varied start color formats. Ensure that all variations of red output contain the same color copmonents when Normalized: "255 0 0". */
			var redVariations = [
				"rgb(255, 0, 0)",
				"rgba(255,0,0,0.5)",
				"#ff0000",
				"red"
			];

			Velocity.Utilities.each(redVariations, function(i, redVariation) {
				assert.equal(/255 0 0/.test(CSS.Normalizations.registered["color"]("extract", null, redVariation)), true, "Normalizations.extractValue() returned red color variation #" + i + ".");
			});

			var testPropertyFake = "fakeProperty";

			/* Property name functions. */
			assert.equal(CSS.Names.prefixCheck(testPropertyFake)[0], testPropertyFake, "Names.prefixCheck() returned unmatched property untouched.");
			assert.equal(CSS.Names.prefixCheck(testPropertyFake)[1], false, "Names.prefixCheck() indicated that unmatched property waws unmatched.");
			assert.equal(CSS.Values.isCSSNullValue("rgba(0,0,0,0)"), true, "Values.isCSSNullValue() matched null value #1.");
			assert.equal(CSS.Values.isCSSNullValue("none"), true, "Values.isCSSNullValue() matched null value #2.");
			assert.equal(CSS.Values.isCSSNullValue(10), false, "Values.isCSSNullValue() didn't match non-null value.");

			var testUnitProperty1 = "rotateZ",
				testUnitPropertyUnit1 = "deg",
				testUnitProperty2 = "width",
				testUnitPropertyUnit2 = "px",
				testElementType1 = document.createElement("div"),
				testElementTypeDisplay1 = "block",
				testElementType2 = document.createElement("span"),
				testElementTypeDisplay2 = "inline";

			/* CSS value functions. */
			assert.equal(CSS.Values.getUnitType(testUnitProperty1), testUnitPropertyUnit1, "Unit type #1 was retrieved.");
			assert.equal(CSS.Values.getUnitType(testUnitProperty2), testUnitPropertyUnit2, "Unit type #2 was retrieved.");

			/* Class addition/removal. */
			var $target1 = getTarget();
			$target1.className = "";
			CSS.Values.addClass($target1, "one");
			assert.equal($target1.className, "one", "First class was added.");
			CSS.Values.addClass($target1, "two");
			assert.equal($target1.className, "one two", "Second class was added.");

			CSS.Values.removeClass($target1, "two");
			assert.equal($target1.className.replace(/^\s+|\s+$/g, ""), "one", "Second class was removed.");
			CSS.Values.removeClass($target1, "one");
			assert.equal($target1.className.replace(/^\s+|\s+$/g, ""), "", "First class was removed.");
		});

		/****************************
		   Start Value Calculation
		****************************/

		QUnit.test("Start Value Calculation", function(assert) {
			var testStartValues = { paddingLeft: "10px", height: "100px", paddingRight: "50%", marginLeft: "100px", marginBottom: "33%", marginTop: "100px", lineHeight: "30px", wordSpacing: "40px", backgroundColorRed: "123" };

			/* Properties not previously defined on the element. */
			var $target1 = getTarget();
			Velocity($target1, testStartValues);
			assert.equal(Data($target1, pluginName).tweensContainer.paddingLeft.startValue, 0, "Undefined standard start value was calculated.");
			assert.equal(Data($target1, pluginName).tweensContainer.backgroundColorRed.startValue, 255, "Undefined start value hook was calculated.");

			/* Properties previously defined on the element. */
			var $target2 = getTarget();
			Velocity($target2, defaultProperties);
			assert.equal(Data($target2, pluginName).tweensContainer.width.startValue, parseFloat(defaultStyles.width), "Defined start value #1 was calculated.");
			assert.equal(Data($target2, pluginName).tweensContainer.opacity.startValue, parseFloat(defaultStyles.opacity), "Defined start value #2 was calculated.");
			assert.equal(Data($target2, pluginName).tweensContainer.colorGreen.startValue, parseFloat(defaultStyles.colorGreen), "Defined hooked start value was calculated.");

			/* Properties that shouldn't cause start values to be unit-converted. */
			var testPropertiesEndNoConvert = { paddingLeft: "20px", height: "40px", paddingRight: "75%" };
			var $target3 = getTarget();
			applyStartValues($target3, testStartValues);
			Velocity($target3, testPropertiesEndNoConvert);
			assert.equal(Data($target3, pluginName).tweensContainer.paddingLeft.startValue, parseFloat(testStartValues.paddingLeft), "Start value #1 wasn't unit converted.");
			assert.equal(Data($target3, pluginName).tweensContainer.height.startValue, parseFloat(testStartValues.height), "Start value #2 wasn't unit converted.");
//			assert.deepEqual(Data($target3, pluginName).tweensContainer.paddingRight.startValue, [Math.floor((parentWidth * parseFloat(testStartValues.paddingRight)) / 100), 0], "Start value #3 was pattern matched.");

			/* Properties that should cause start values to be unit-converted. */
			var testPropertiesEndConvert = { paddingLeft: "20%", height: "40%", lineHeight: "0.5em", wordSpacing: "2rem", marginLeft: "10vw", marginTop: "5vh", marginBottom: "100px" };
				parentWidth = $qunitStage.clientWidth,
				parentHeight = $qunitStage.clientHeight,
				parentFontSize = Velocity.CSS.getPropertyValue($qunitStage, "fontSize"),
				remSize = parseFloat(Velocity.CSS.getPropertyValue(document.body, "fontSize"));

			var $target4 = getTarget();
			applyStartValues($target4, testStartValues);
			Velocity($target4, testPropertiesEndConvert);

			/* Long decimal results can be returned after unit conversion, and Velocity's code and the code here can differ in precision. So, we round floor values before comparison. */
//			assert.deepEqual(Data($target4, pluginName).tweensContainer.paddingLeft.startValue, [parseFloat(testStartValues.paddingLeft), 0], "Horizontal property converted to %.");
			assert.equal(Math.floor(Data($target4, pluginName).tweensContainer.height.startValue), Math.floor((parseFloat(testStartValues.height) / parentHeight) * 100), "Vertical property converted to %.");
//			assert.equal(Data($target4, pluginName).tweensContainer.lineHeight.startValue, Math.floor(parseFloat(testStartValues.lineHeight) / parseFloat(parentFontSize)), "Property converted to em.");
//			assert.equal(Data($target4, pluginName).tweensContainer.wordSpacing.startValue, Math.floor(parseFloat(testStartValues.wordSpacing) / parseFloat(remSize)), "Property converted to rem.");
			assert.equal(Math.floor(Data($target4, pluginName).tweensContainer.marginBottom.startValue), parseFloat(testStartValues.marginBottom) / 100 * parseFloat($target4.parentNode.offsetWidth), "Property converted to px.");

//			if (!(IE<=8) && !isAndroid) {
//				assert.equal(Data($target4, pluginName).tweensContainer.marginLeft.startValue, Math.floor(parseFloat(testStartValues.marginLeft) / window.innerWidth * 100), "Horizontal property converted to vw.");
//				assert.equal(Data($target4, pluginName).tweensContainer.marginTop.startValue, Math.floor(parseFloat(testStartValues.marginTop) / window.innerHeight * 100), "Vertical property converted to vh.");
//			}

			// TODO: Tests for auto-parameters as the units are no longer converted.

  			/* jQuery TRBL deferring. */
			var testPropertiesTRBL = { left: "1000px" };
			var $TRBLContainer = document.createElement("div");

			$TRBLContainer.setAttribute("id", "TRBLContainer");
			$TRBLContainer.style.marginLeft = testPropertiesTRBL.left;
			$TRBLContainer.style.width = "100px";
			$TRBLContainer.style.height = "100px";
  			document.body.appendChild($TRBLContainer);

			var $target5 = getTarget();
			$target5.style.position = "absolute";
  			$TRBLContainer.appendChild($target5);
			Velocity($target5, testPropertiesTRBL);

			assert.equal(Math.round(Data($target5, pluginName).tweensContainer.left.startValue), Math.round(parseFloat(testPropertiesTRBL.left) + parseFloat(Velocity.CSS.getPropertyValue(document.body, "marginLeft"))), "TRBL value was deferred to jQuery.");
		});

		/**************************
		   End Value Calculation
		**************************/

		QUnit.asyncTest("End Value Calculation", function(assert) {
			/* Standard properties without operators. */
			var $target1 = getTarget();
			Velocity($target1, defaultProperties);
			setTimeout(function() {
				assert.equal(Data($target1, pluginName).tweensContainer.width.endValue, defaultProperties.width, "Standard end value #1 was calculated.");
				assert.equal(Data($target1, pluginName).tweensContainer.opacity.endValue, defaultProperties.opacity, "Standard end value #2 was calculated.");
			}, asyncCheckDuration);

			/* Standard properties with operators. */
			var testIncrementWidth = "5px",
				testDecrementOpacity = 0.25,
				testMultiplyMarginBottom = 4,
				testDivideHeight = 2;

			var $target2 = getTarget();
			Velocity($target2, { width: "+=" + testIncrementWidth, opacity: "-=" + testDecrementOpacity, marginBottom: "*=" + testMultiplyMarginBottom, height: "/=" + testDivideHeight });
			setTimeout(function() {

				assert.equal(Data($target2, pluginName).tweensContainer.width.endValue, defaultStyles.width + parseFloat(testIncrementWidth), "Incremented end value was calculated.");
				assert.equal(Data($target2, pluginName).tweensContainer.opacity.endValue, defaultStyles.opacity - testDecrementOpacity, "Decremented end value was calculated.");
				assert.equal(Data($target2, pluginName).tweensContainer.marginBottom.endValue, defaultStyles.marginBottom * testMultiplyMarginBottom, "Multiplied end value was calculated.");
				assert.equal(Data($target2, pluginName).tweensContainer.height.endValue, defaultStyles.height / testDivideHeight, "Divided end value was calculated.");

				start();
			}, asyncCheckDuration);
		});

		/**********************
		   End Value Setting
		**********************/

		QUnit.asyncTest("End Value Setting (Note: Browser Tab Must Have Focus Due to rAF)", function(assert) {
			/* Transforms and the properties that are hooked by Velocity aren't supported below IE9. */
			if (!(IE < 9)) {
				var testHooks = { 
					boxShadowBlur: "10px", // "black 0px 0px 10px 0px"
					boxShadowSpread: "20px", // "black 0px 0px 0px 20px"
					textShadowBlur: "30px" // "black 0px 0px 30px"
				};

				/* Hooks. */
				var $target3 = getTarget();
				Velocity($target3, testHooks);
				setTimeout(function() {
					/* Check for a match anywhere in the string since browser differ in where they inject the color value. */
					assert.equal(/0px 0px 10px 20px/.test(Velocity.CSS.getPropertyValue($target3, "boxShadow")), true, "Hook end value #1 was set.");
					/* textShadow isn't supported below IE10. */
					if (!IE || IE >= 10) {
						assert.equal(/0px 0px 30px/.test(Velocity.CSS.getPropertyValue($target3, "textShadow")), true, "Hook end value #2 was set.");
					}
				}, completeCheckDuration);

				if (!(IE < 10) && !Velocity.State.isGingerbread) {
					var testTransforms = { 
							translateY: "10em", // Should stay the same
							translateX: "20px", // Should stay the same
							scaleX: "1.50", // Should remain unitless
							translateZ: "30", // Should become "10px"
							scaleY: "1.50deg" // Should be ignored entirely since it uses an invalid unit
						},
						testTransformsOutput = "matrix3d(1.5, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 20, 160, 30, 1)";

					/* Transforms. */
					var $target4 = getTarget();
					Velocity($target4, testTransforms);
					setTimeout(function() {
						/* Check for a match anywhere in the string since browser differ in where they inject the color value. */
						assert.equal(Velocity.CSS.getPropertyValue($target4, "transform"), testTransformsOutput, "Transform end value was set.");

						/* Ensure previous transform values are reused. */
						Velocity($target4, { translateX: parseFloat(testTransforms.translateX) / 2 });
						assert.equal(Data($target4, pluginName).tweensContainer.translateX.startValue, parseFloat(testTransforms.translateX), "Previous transform value was reused.");
					}, completeCheckDuration);
				}

				if (!Velocity.State.isGingerbread) {
					/* SVG. */
					var $svgRoot = document.createElementNS("http://www.w3.org/2000/svg", "svg"),
						$svgRect = document.createElementNS("http://www.w3.org/2000/svg", "rect"),
						svgStartValues = { x: 100, y: 10, width: 250, height: "30%" },
						svgEndValues = { x: 200, width: "50%", strokeDasharray: 10, height: "40%", rotateZ: "90deg", rotateX: "45deg" };

					$svgRoot.setAttribute("width", 1000);
					$svgRoot.setAttribute("height", 1000);

					$svgRect.setAttribute("x", svgStartValues.x);
					$svgRect.setAttribute("y", svgStartValues.y);
					$svgRect.setAttribute("width", svgStartValues.width);
					$svgRect.setAttribute("height", svgStartValues.height);

					$svgRoot.appendChild($svgRect);
					$qunitStage.appendChild($svgRoot);

					Velocity($svgRect, svgEndValues, defaultOptions);
					setTimeout(function() {
						assert.equal(Math.round(Data($svgRect, pluginName).tweensContainer.x.startValue), svgStartValues.x, "SVG dimensional attribute #1 value was retrieved.");
						assert.equal(Math.round(parseFloat(Velocity.CSS.getPropertyValue($svgRect, "x"))), svgEndValues.x, "SVG dimensional attribute #1 end value was set.");

						assert.equal(Math.round(Data($svgRect, pluginName).tweensContainer.width.startValue), parseFloat(svgStartValues.width)/1000 * 100, "SVG dimensional attribute #2 value was retrieved.");
						assert.equal(Math.round(parseFloat(Velocity.CSS.getPropertyValue($svgRect, "width"))), parseFloat(svgEndValues.width)/100 * 1000, "SVG dimensional attribute #2 end value was set.");

						assert.equal(Math.round(Data($svgRect, pluginName).tweensContainer.height.startValue), parseFloat(svgStartValues.height), "SVG dimensional attribute #3 value was retrieved.");
						assert.equal(Math.round(parseFloat(Velocity.CSS.getPropertyValue($svgRect, "height"))), parseFloat(svgEndValues.height)/100 * 1000, "SVG dimensional attribute #3 end value was set.");

						assert.equal(Math.round(Data($svgRect, pluginName).tweensContainer.rotateZ.startValue), 0, "SVG 2D transform value was retrieved.");
						assert.equal(Math.round(parseFloat(Velocity.CSS.getPropertyValue($svgRect, "rotateZ"))), parseFloat(svgEndValues.rotateZ), "SVG 2D transform end value was set.");

						if (!IE) {
							assert.equal(Math.round(Data($svgRect, pluginName).tweensContainer.rotateX.startValue), 0, "SVG 3D transform value was retrieved.");
							assert.equal(Math.round(parseFloat(Velocity.CSS.getPropertyValue($svgRect, "rotateX"))), parseFloat(svgEndValues.rotateX), "SVG 3D transform end value was set.");
						}

						assert.equal(Math.round(Data($svgRect, pluginName).tweensContainer.strokeDasharray.startValue), 0, "SVG CSS style value was retrieved.");
						assert.equal(parseFloat(Velocity.CSS.getPropertyValue($svgRect, "strokeDasharray")), svgEndValues.strokeDasharray, "SVG CSS style end value was set.");
					}, completeCheckDuration);
				}
			}

			/* Standard properties. */
			var $target1 = getTarget();
			Velocity($target1, defaultProperties, { });
			setTimeout(function() {
				assert.equal(parseFloat(Velocity.CSS.getPropertyValue($target1, "width")), defaultProperties.width, "Standard end value #1 was set.");
				assert.equal(parseFloat(Velocity.CSS.getPropertyValue($target1, "opacity")), defaultProperties.opacity, "Standard end value #2 was set.");

				start();
			}, completeCheckDuration);
		});

		/**********************
		   End Value Caching
		**********************/

		QUnit.asyncTest("End Value Caching", function(assert) {
			expect(4);

			var newProperties = { height: "50px", width: "250px" };

			var $target1 = getTarget();
			Velocity($target1, defaultProperties, function() {

				applyStartValues($target1, newProperties);
				/* Called after the last call is complete (stale). Ensure that the newly-set (via $.css()) properties are used. */
				Velocity($target1, defaultProperties);

				setTimeout(function() {
					assert.equal(Data($target1, pluginName).tweensContainer.width.startValue, parseFloat(newProperties.width), "Stale end value #1 wasn't pulled.");
					assert.equal(Data($target1, pluginName).tweensContainer.height.startValue, parseFloat(newProperties.height), "Stale end value #2 wasn't pulled.");
				}, asyncCheckDuration);
			});

			var $target2 = getTarget();
			Velocity($target2, defaultProperties);
			Velocity($target2, newProperties, function() {
				/* Chained onto a previous call (fresh). */
				assert.equal(Data($target2, pluginName).tweensContainer.width.startValue, defaultProperties.width, "Chained end value #1 was pulled.");
				assert.equal(Data($target2, pluginName).tweensContainer.height.startValue, defaultProperties.height, "Chained end value #2 was pulled.");

				start();
			});
		});

		/****************
		    Queueing
		****************/

		QUnit.asyncTest("Queueing", function(assert) {
			expect(1);

			var $target1 = getTarget();
			Velocity($target1, { opacity: 0 });
			Velocity($target1, { width: 2 });

			setTimeout(function() {
				/* Ensure that the second call hasn't started yet. */
				assert.equal(parseFloat(Velocity.CSS.getPropertyValue($target1, "width")), defaultStyles.width, "Queued calls chain.");

				start();
			}, asyncCheckDuration);
		});

		/******************
		   Option: Queue
		******************/

		QUnit.asyncTest("Option: Queue", function(assert) {
			expect(5);

			var testQueue = "custom";

			var $target1 = getTarget();
			Velocity($target1, defaultProperties, { queue: testQueue });
			Velocity($target1, defaultProperties, { queue: testQueue });
			Velocity($target1, defaultProperties, { queue: testQueue });

			assert.equal(Velocity.Utilities.queue($target1, testQueue).length, 3, "Custom queue was appended to.");
			assert.equal(Data($target1, pluginName).isAnimating, false, "Custom queue didn't auto-dequeue.");

			Velocity.Utilities.dequeue($target1, testQueue);
			assert.equal(Data($target1, pluginName).isAnimating, true, "Dequeue custom queue.");

			Velocity($target1, "stop", testQueue);
			assert.equal(Velocity.Utilities.queue($target1, testQueue).length, 0, "Stopped custom queue.");

			var $target2 = getTarget();
			Velocity($target2, { opacity: 0 });
			Velocity($target2, { width: 10 }, { queue: false });

			setTimeout(function() {
				/* Ensure that the second call starts immediately. */
				notEqual(Velocity.CSS.getPropertyValue($target2, "width"), defaultStyles.width, "Parallel calls don't queue.");

				start();
			}, asyncCheckDuration);
		});

		/* Helpful redirect for testing custom and parallel queues. */
		// var $div2 = $("#DataBody-PropertiesDummy");
		// $.fn.velocity.defaults.duration = 1000;
		// $div2.velocity("scroll", { queue: "test" })
		// $div2.velocity({width: 100}, { queue: "test" })
		// $div2.velocity({ borderWidth: 50 }, { queue: "test" })
		// $div2.velocity({height: 20}, { queue: "test" })
		// $div2.velocity({marginLeft: 200}, { queue: "test" })
		// $div2.velocity({paddingTop: 60});
		// $div2.velocity({marginTop: 100});
		// $div2.velocity({paddingRight: 40});
		// $div2.velocity({marginTop: 0})
		// $div2.dequeue("test")

		/******************
		   Option: Delay   
		******************/

		QUnit.asyncTest("Option: Delay (Note: Browser Tab Must Have Focus Due to rAF)", function(assert) {
			expect(2);

			var testDelay = defaultOptions.duration * 2;

			var $target = getTarget();
			Velocity($target, defaultProperties, { delay: testDelay });

			setTimeout(function() {
				assert.equal(parseFloat(Velocity.CSS.getPropertyValue($target, "width")), defaultStyles.width, "Delayed calls don't start immediately");
			}, asyncCheckDuration);

			setTimeout(function() {
				assert.equal(parseFloat(Velocity.CSS.getPropertyValue($target, "width")), defaultProperties.width, "Delayed calls eventually start.");

				start();
			}, completeCheckDuration + testDelay);
		});

		/********************
		   Option: Easing   
		********************/

		QUnit.asyncTest("Option: Easing", function(assert) {
			expect(5);

			var $target1 = getTarget();
			/* Ensure that a fake easing doesn't throw an error. */
			Velocity($target1, defaultProperties, { easing: "fake" });
			assert.equal(true, true, "Fake easing didn't throw error.");

			/* Ensure that an improperly-formmated bezier curve array doesn't throw an error. */
			var $target2 = getTarget();
			Velocity($target2, defaultProperties, { easing: [ "a", 0.5, 0.5, 0.5 ] });
			Velocity($target2, defaultProperties, { easing: [ 0.5, 0.5, 0.5 ] });
			assert.equal(true, true, "Invalid bezier curve didn't throw error.");

			/* Ensure that a properly-formatted bezier curve array returns a bezier function. */
			var $target3 = getTarget();
			var easingBezierArray = [ 0.27, -0.65, 0.78, 0.19 ],
				easingBezierTestPercent = 0.25,
				easingBezierTestValue = /^-0\.23/;

			Velocity($target3, defaultProperties, { easing: easingBezierArray });
			setTimeout(function() {
				assert.equal(easingBezierTestValue.test(Data($target3, pluginName).tweensContainer.width.easing(easingBezierTestPercent)), true, "Array converted into bezier function.");
			}, asyncCheckDuration);

			/* Ensure that a properly-formatted spring RK4 array returns a bezier function. */
			var $target4 = getTarget();
			var easingSpringRK4Array = [ 250, 12 ],
				easingSpringRK4TestPercent = 0.25,
				easingSpringRK4TestValue = /^1\.21/;

			Velocity($target4, defaultProperties, { easing: easingSpringRK4Array });
			setTimeout(function() {
				assert.equal(easingSpringRK4TestValue.test(Data($target4, pluginName).tweensContainer.width.easing(easingSpringRK4TestPercent)), true, "Array converted into springRK4 function.");
			}, asyncCheckDuration);

			/* Ensure that a properly-formatted step easing array returns a step function. */
			var $target5 = getTarget();
			var easingStepArray = [ 4 ],
				easingStepTestPercent = 0.35,
				easingStepTestValue = /^0\.25/;

			Velocity($target5, defaultProperties, { easing: easingStepArray });
			setTimeout(function() {
				assert.equal(easingStepTestValue.test(Data($target5, pluginName).tweensContainer.width.easing(easingStepTestPercent)), true, "Array converted into Step function.");

				start();
			}, asyncCheckDuration);
		});

		/********************
		   Option: Display   
		********************/

		QUnit.asyncTest("Option: Display", function(assert) {
			var testDisplayBlock = "block",
				testDisplayNone = "none",
				testDisplayBlank = "";

			var $target1 = getTarget();
			/* Async checks are used since the display property is set inside processCallsTick(). */
			Velocity($target1, defaultProperties, { display: testDisplayBlock });
			setTimeout(function() {
				assert.equal(Velocity.CSS.getPropertyValue($target1, "display"), testDisplayBlock, "Display:'block' was set immediately.");
			}, asyncCheckDuration);

			var $target2 = getTarget();
			Velocity($target2, defaultProperties, { display: testDisplayNone });
			setTimeout(function() {
				notEqual(Velocity.CSS.getPropertyValue($target2, "display"), 0, "Display:'none' was not set immediately.");
			}, asyncCheckDuration);
			setTimeout(function() {
				assert.equal(Velocity.CSS.getPropertyValue($target2, "display"), 0, "Display:'none' was set upon completion.");
			}, completeCheckDuration);

			var $target3 = getTarget();
			Velocity($target3, defaultProperties, { display: testDisplayBlank });
			setTimeout(function() {
				assert.equal(Velocity.CSS.getPropertyValue($target3, "display"), "block", "Display:'' was set immediately.");
				
				start();
			}, completeCheckDuration);
		});

		/***********************
		   Option: Visibility   
		***********************/

		QUnit.asyncTest("Option: Visibility", function(assert) {
			var testVisibilityBlock = "visible",
				testVisibilityNone = "hidden",
				testVisibilityBlank = "";

			var $target1 = getTarget();
			/* Async checks are used since the visibility property is set inside processCallsTick(). */
			Velocity($target1, defaultProperties, { visibility: testVisibilityBlock });
			setTimeout(function() {
				assert.equal(Velocity.CSS.getPropertyValue($target1, "visibility"), testVisibilityBlock, "visibility:'visible' was set immediately.");
			}, asyncCheckDuration);

			var $target2 = getTarget();
			Velocity($target2, defaultProperties, { visibility: testVisibilityNone });
			setTimeout(function() {
				notEqual(Velocity.CSS.getPropertyValue($target2, "visibility"), 0, "visibility:'hidden' was not set immediately.");
			}, asyncCheckDuration);
			setTimeout(function() {
				assert.equal(Velocity.CSS.getPropertyValue($target2, "visibility"), "hidden", "visibility:'hidden' was set upon completion.");
			}, completeCheckDuration);

			var $target3 = getTarget();
			Velocity($target3, defaultProperties, { display: testVisibilityBlank });
			setTimeout(function() {
				assert.equal(/visible|inherit/.test(Velocity.CSS.getPropertyValue($target3, "visibility")), true, "visibility:'' was set immediately.");
				
				start();
			}, completeCheckDuration);
		});

		/******************
		   Option: Loop
		******************/

		QUnit.asyncTest("Option: Loop", function(assert) {
			expect(6);

			var testOptions = { delay: 500, easing: "spring" };

			var $target1 = getTarget();
			Velocity($target1, defaultProperties, { loop: 2, delay: testOptions.delay, easing: testOptions.easing });

			/* We expect 1 delay followed by 1 call for a total of 4 cycles, which equates to 8 queue items. */
			assert.equal(Velocity.Utilities.queue($target1).length, 8, "Loop call produced 'reverse' calls.");

			var $target2 = getTarget();
			Velocity($target2, defaultProperties, { loop: true, delay: testOptions.delay, easing: testOptions.easing });
			setTimeout(function() {
				assert.equal(Data($target1, pluginName).opts.delay, testOptions.delay, "Delay option was passed into second loop call (jQuery object).");
				assert.equal(Data($target1, pluginName).opts.easing, testOptions.easing, "Easing option was passed into second loop call (jQuery object).");

				assert.equal(Data($target2, pluginName).opts.delay, testOptions.delay, "Delay option was passed into second infinite loop call (jQuery object).");
				assert.equal(Data($target2, pluginName).opts.easing, testOptions.easing, "Easing option was passed into second infinite loop call (jQuery object).");

				assert.equal(Velocity.Utilities.queue($target2).length, 2, "Infinite loop is running.");

				start();
			}, completeCheckDuration + testOptions.delay);
		});

		/*******************
		   Option: Begin
		*******************/

		QUnit.asyncTest("Option: Begin", function(assert) {
			expect(1);

			var $targetSet = [ getTarget(), getTarget() ];
			Velocity($targetSet, defaultProperties, { 
				duration: asyncCheckDuration,
				begin: function() {
					assert.deepEqual(this, $targetSet, "Elements passed into callback.");

					start();
				}
			});
		});

		/*********************
		   Option: Complete
		*********************/

		QUnit.asyncTest("Option: Complete", function(assert) {
			expect(1);
			
			var $targetSet = [ getTarget(), getTarget() ];
			Velocity($targetSet, defaultProperties, { 
				duration: asyncCheckDuration,
				complete: function() {
					assert.deepEqual(this, $targetSet, "Elements passed into callback.");

					start();
				}
			});
		});

		/*********************
		   Option: Progress
		*********************/

		QUnit.asyncTest("Option: Progress", function(assert) {
			expect(3);

			var alreadyCalled = false;

			var $target = getTarget();
			Velocity($target, defaultProperties, { 
				duration: asyncCheckDuration,
				progress: function(elements, percentComplete, msRemaining) {
					if (!alreadyCalled) {
						assert.deepEqual(this, [ $target ], "Elements passed into progress.");
						assert.equal(percentComplete >= 0 && percentComplete <= 1, true, "percentComplete passed into progress.");
						assert.equal(msRemaining > asyncCheckDuration - 50, true, "msRemaining passed into progress.");

						alreadyCalled = true;
						start();
					}
				}
			});
		});

		/*********************
		   Command: Reverse
		*********************/

		QUnit.asyncTest("Command: Reverse", function(assert) {
			expect(5);

			var testEasing = "spring";

			var $target = getTarget();
			/* Ensure an error isn't thrown when there's no previous animation to reverse to. */
			Velocity($target, "reverse");
			Velocity($target, { opacity: defaultProperties.opacity, width: defaultProperties.width }, { easing: testEasing });
			Velocity($target, "reverse", function() {
				assert.equal(parseFloat(Velocity.CSS.getPropertyValue($target, "opacity")), defaultStyles.opacity, "Reversed to initial property #1.");
				assert.equal(parseFloat(Velocity.CSS.getPropertyValue($target, "width")), defaultStyles.width, "Reversed to initial property #2.");
			});
			/* Check chained reverses. */
			Velocity($target, "reverse", function() {
				assert.equal(parseFloat(Velocity.CSS.getPropertyValue($target, "opacity")), defaultProperties.opacity, "Reversed to reversed property #1.");
				assert.equal(parseFloat(Velocity.CSS.getPropertyValue($target, "width")), defaultProperties.width, "Reversed to reversed property #2.");

				/* Ensure the options were passed through until the end. */
				assert.equal(Data($target, pluginName).opts.easing, testEasing, "Options object passed through.");

				start();
			});
		});

		/******************
		   Command: Stop
		******************/

		QUnit.asyncTest("Command: Stop", function(assert) {
			expect(4);

			var $target1 = getTarget();
			/* Ensure an error isn't thrown when "stop" is called on a $target that isn't animating. */
			Velocity($target1, "stop");
			Velocity($target1, defaultProperties, defaultOptions);
			Velocity($target1, { top: 0 }, defaultOptions);
			Velocity($target1, { width: 0 }, defaultOptions);
			Velocity($target1, "stop", true);

			/* Ensure "stop" has removed all queued animations. */
			/* We're using the element's queue length as a proxy. 0 and 1 both mean that the element's queue has been cleared -- a length of 1 just indicates that the animation is in progress. */
			setTimeout(function() {
				assert.equal(Velocity.Utilities.queue($target1).length <= 1, true, "Queue cleared.");
			}, 1);

			var $target2 = getTarget();
			Velocity($target2, { opacity: 0 }, Velocity.Utilities.extend({}, defaultOptions, { delay: 1000 }));
			Velocity($target2, { width: 0 }, defaultOptions);
			Velocity($target2, "stop");

			var $target3 = getTarget();
			Velocity($target3, { opacity: 0 }, Velocity.Utilities.extend({}, defaultOptions, { delay: 1000 }));
			Velocity($target3, { width: 0 }, defaultOptions);
			Velocity($target3, { width: 100 }, defaultOptions);
			Velocity($target3, "stop", true);

			setTimeout(function() {
				assert.equal(Data($target2, pluginName).tweensContainer.opacity, undefined, "Active call stopped.");
				notEqual(Data($target2, pluginName).tweensContainer.width, undefined, "Next queue item started.");

				assert.equal(Velocity.Utilities.queue($target3, "").length, 0, "Full queue array cleared.");

				start();
			}, asyncCheckDuration);
		});

		/****************************
		   Command: Pause / Resume
		*****************************/

		QUnit.asyncTest("Command: Pause / Resume", function() {
			expect(10);

			var $target1 = getTarget(); 
			var $target1d = getTarget(); //delayed
			/* Ensure an error isn't thrown when "pause" is called on a $target that isn't animating. */
			Velocity($target1, "pause");
			Velocity($target1d, "pause");

			/* Ensure an error isn't thrown when "pause" is called on a $target that isn't animating. */
			Velocity($target1, "resume");
			Velocity($target1d, "resume");

			/* Ensure a paused $target ceases to animate */
			Velocity($target1, { opacity: 0 }, defaultOptions);
			notEqual(Data($target1, pluginName).isPaused, true, "Newly active call not paused.");
			Velocity($target1d, { opacity: 0 }, Velocity.Utilities.extend({}, defaultOptions, { delay: 200 }));
			notEqual(Data($target1d, pluginName).isPaused, true, "New call with delay not paused.");
			
			Velocity($target1, "pause");
			Velocity($target1d, "pause");

			setTimeout(function() {
				equal(parseFloat(Velocity.CSS.getPropertyValue($target1, "opacity")), 1, "Property value unchanged after pause.");
			}, completeCheckDuration);

			setTimeout(function() {
				equal(parseFloat(Velocity.CSS.getPropertyValue($target1d, "opacity")), 1, "Property value unchanged after pause during delay.");
			}, 201);

			/* Ensure a resumed $target proceeds to animate */
			var $target2 = getTarget();
			var $target2d = getTarget();
			Velocity($target2, { opacity: 0 }, defaultOptions);
			Velocity($target2d, { opacity: 0 }, Velocity.Utilities.extend({}, defaultOptions, { delay: 100 }));
			
			Velocity($target2, "pause");
			
			setTimeout(function() {
				Velocity($target2d, "pause");
			}, 40);

			Velocity($target2, "resume");

			setTimeout(function() {
				Velocity($target2d, "resume");
			}, 80);

			setTimeout(function() {
				equal(parseFloat(Velocity.CSS.getPropertyValue($target2, "opacity")), 0, "Tween completed after pause/resume.");
			}, completeCheckDuration);

			setTimeout(function() {
				equal(parseFloat(Velocity.CSS.getPropertyValue($target2d, "opacity")), 1, "Delayed tween did not start early after pause.");
			}, 130);

			setTimeout(function() {
				equal(parseFloat(Velocity.CSS.getPropertyValue($target2d, "opacity")), 0, "Delayed tween completed after pause/resume.");
			}, completeCheckDuration + 200);

			/* Ensure the property values of a pause tween are midway between start and end values */
			var $target3 = getTarget(),
				percent = 0,
				isPaused = false;

			Velocity($target3, { opacity: 0 }, { 
				duration: 200,
				easing:"linear",
				progress: function(elements, _percentComplete, _msRemaining) {
					if(isPaused) {
						throw new Error("Progress callback run after pause.");
					}
					percent = _percentComplete;
				}
			});

			/* Pause element midway through tween */
			setTimeout(function() {
				Velocity($target3, "pause");
				isPaused = true;
			}, 100);

			setTimeout(function() {
				Velocity($target3, "resume");
				isPaused = false;
			}, 200);

			
			setTimeout(function() {
				/* Property value should be linearly proportional to */
				var val = parseFloat(Velocity.CSS.getPropertyValue($target3, "opacity"));

				/* Prop value and percentage complete should correlate after pause. We need to test this since
				the timing variables used to calculate and return the percentage complete and msRemaining are
				modified after pause and resume comamands have been issued on the call */
				ok(Math.round(1 - val, 4) == Math.round(percent, 4) , "Tween value and percentageComplete correlate correctly after pause.");

			}, 250);


			/* Ensure a all elements in a call are paused if any element is paused, likewise for resume */
			var $targetA = getTarget(), $targetB = getTarget();
			Velocity([$targetA, $targetB], { opacity: 0 }, {
				duration:100,
				progress:function(elements, percent, msRemaining) {
					throw new Error("Tween does not proceed for any elements");
				}
			});
			
			Velocity($targetA, "pause");	

			/* Ensure proper behavior with queue:false  */
			var $target4 = getTarget();
			Velocity($target4, { opacity: 0 }, { 
				duration: 200,
			});

			var isResumed = false;

			setTimeout(function() {
				Velocity($target4, "pause");
				Velocity($target4, { left: -20 }, { 
					duration: 100,
					easing:"linear",
					queue: false,
					begin: function(elements) {
						ok(true, "Animation with {queue:false} will run regardless of previously paused animations.")
					}
				});

				Velocity($target4, { top: 20 }, { 
					duration: 100,
					easing:"linear",
					begin: function(elements) {
						if(!isResumed) {
							throw new Error("Queued animation doesn't begin until previous animation was resumed.");
						} else {
							ok(true, "Queued animation began after previously paused animation completed");
						}
					}
				});
			}, 100);

			setTimeout(function() {
				isResumed = true;
				Velocity($target4, "resume");
			}, 200);

			setTimeout(function() {
				start();
				/* Clear out any existing test animations to prevent errors from being thrown
				in another test */
				try {
					Velocity([$targetA, $target3, $target4], "stop");
				} catch (e) {}
			}, 800);

		});

		/*********************************
		   Command: PauseAll / ResumeAll
		**********************************/

		QUnit.asyncTest("Command: Global Pause / Resume", function() {
			expect(3);

			var $target1 = getTarget(); 
			var $target2 = getTarget();
			var $target3 = getTarget();
			var $target4 = getTarget();

			var isPaused = false;
			var hasProgressed2 = false;
			Velocity($target1, { opacity: 0 }, Velocity.Utilities.extend({}, defaultOptions, { 
				delay: 100,
				queue:false,
				progress: function(elements, progress, msRemaining) {
					if(isPaused) {
						throw new Error("Delayed Tween should not progress when globally paused");
					}
				}
			}));
			
			Velocity($target2, { opacity: 0 }, Velocity.Utilities.extend({}, defaultOptions, { 
				progress: function(elements, progress, msRemaining) {
					if(isPaused) {
						throw new Error("Tween should not progress when globally paused");
					} else if(!hasProgressed2) {
						hasProgressed2 = true;
						ok (true, "Tween resumes on individual pause after global resume");
					}
				}
			}));

			Velocity.pauseAll();
			isPaused = true;

			/* Testing with custom queues */
			var hasProgressed3 = false;
			Velocity($target3, { opacity: 0 }, Velocity.Utilities.extend({}, defaultOptions, { 
				queue: "queue1",
				progress: function(elements, progress, msRemaining) {
					if(!hasProgressed3) {
						hasProgressed3 = true;
						ok (true, "Tweens created after global pause begin immediately");
					}
				}
			}));

			var hasProgressed4 = false;
			Velocity($target4, { opacity: 0 }, Velocity.Utilities.extend({}, defaultOptions, { 
				queue: "queue2",
				progress: function(elements, progress, msRemaining) {
					if(isPaused) {
						throw new Error("Tween on paused queue should not progress");
					} else if(!hasProgressed4) {
						hasProgressed4 = true;
						ok (true, "Paused tweens on a queue resume after a global resumeAll call");
					}
				}
			}));

			/* Begin queued animations */
			Velocity.Utilities.dequeue($target4, "queue2");
			Velocity.Utilities.dequeue($target3, "queue1");
			
			/* Only $target4 should pause */
			Velocity.pauseAll("queue2");

			setTimeout(function() {
				isPaused = false;
				Velocity.resumeAll();
			}, 200);

			setTimeout(function() {
				start();
				Velocity.resumeAll();
			}, 400);

		});

		/******************
		   Command: Finish
		******************/

		QUnit.asyncTest("Command: Finish / FinishAll", function(assert) {
			expect(9);

			var $target1 = getTarget();
			/* Ensure an error isn't thrown when "finish" is called on a $target that isn't animating. */
			Velocity($target1, "finish");

			/* Animate to defaultProperties, and then "finish" to jump to the end of it. */
			Velocity($target1, defaultProperties, Velocity.Utilities.extend({}, defaultOptions, { delay: 1000}));
			Velocity($target1, "finish");

			setTimeout(function() {
				/* Ensure "finish" has removed all queued animations. */
				/* We're using the element's queue length as a proxy. 0 and 1 both mean that the element's queue has been cleared -- a length of 1 just indicates that the animation is in progress. */
				assert.equal(Velocity.Utilities.queue($target1).length <= 1, true, "Queue cleared.");

				/* End result of the animation should be applied */
				assert.equal(parseFloat(Velocity.CSS.getPropertyValue($target1, "width")), defaultProperties.width, "Standard end value #1 was set.");
				assert.equal(parseFloat(Velocity.CSS.getPropertyValue($target1, "opacity")), defaultProperties.opacity, "Standard end value #2 was set.");
			}, asyncCheckDuration);

			var $target2 = getTarget();
			Velocity($target2, { opacity: 0 }, Velocity.Utilities.extend({}, defaultOptions, { delay: 1000 }));
			Velocity($target2, { width: 0 }, defaultOptions);
			Velocity($target2, "finish");

			var $target3 = getTarget();
			Velocity($target3, { opacity: 0, width: 50 }, Velocity.Utilities.extend({}, defaultOptions, { delay: 1000 }));
			Velocity($target3, { width: 0 }, defaultOptions);
			Velocity($target3, { width: 100 }, defaultOptions);
			Velocity($target3, "finish", true);

			var $target4 = getTarget();
			Velocity($target4, { opacity: 0, width: 50 }, Velocity.Utilities.extend({}, defaultOptions, { delay: 1000 }));
			Velocity($target4, { width: 0 }, defaultOptions);
			Velocity($target4, { width: 100 }, defaultOptions);
			Velocity($target4, "finishAll", true);

			setTimeout(function() {
				assert.equal(Data($target2, pluginName).tweensContainer.opacity, undefined, "Active call stopped.");
				notEqual(Data($target2, pluginName).tweensContainer.width, undefined, "Next queue item started.");

				assert.equal(Velocity.Utilities.queue($target3, "").length, 0, "Full queue array cleared.");
				assert.equal(parseFloat(Velocity.CSS.getPropertyValue($target3, "width")), 50, "Just the first call's width was applied.");

				assert.equal(Velocity.Utilities.queue($target4, "").length, 0, "Full queue array cleared.");
				assert.equal(parseFloat(Velocity.CSS.getPropertyValue($target4, "width")), 100, "The last call's width was applied.");

				start();
			}, asyncCheckDuration);
		});

		/***********************
		   Feature: Redirects
		***********************/

		QUnit.asyncTest("Feature: Redirects", function(assert) {
			var $target1 = getTarget(),
				$target2 = getTarget(),
				redirectOptions = { duration: 1500 };

			(window.jQuery || window.Zepto || window).Velocity.Redirects.test = function (element, options, elementIndex, elementsLength) {
				if (elementIndex === 0) {
					assert.deepEqual(element, $target1, "Element passed through #1.");
					assert.deepEqual(options, redirectOptions, "Options object passed through #1.");
					assert.equal(elementIndex, 0, "Element index passed through #1.");
					assert.equal(elementsLength, 2, "Elements length passed through #1.");
				} else if (elementIndex === 1) {
					assert.deepEqual(element, $target2, "Element passed through #2.");
					assert.deepEqual(options, redirectOptions, "Options object passed through #2.");
					assert.equal(elementIndex, 1, "Element index passed through #2.");
					assert.equal(elementsLength, 2, "Elements length passed through #2.");

					start();
				}
			};

			Velocity([ $target1, $target2 ], "test", redirectOptions);
		});

		/************************
		   Feature: animating
		************************/

		QUnit.asyncTest("Feature: 'velocity-animating' Class", function(assert) {
			var $target1 = getTarget();

			Velocity($target1, defaultProperties, function(element) { 
					assert.equal(/velocity-animating/.test($target1.className), false, "Class removed.");
					start();
				}
			);

			setTimeout(function() {
				assert.equal(/velocity-animating/.test($target1.className), true, "Class added."); 
			}, asyncCheckDuration);
		});

		/***********************
		   Feature: Promises
		***********************/

		QUnit.asyncTest("Feature: Promises", function(assert) {
			expect(5);

			var $target1 = getTarget();

			Velocity($target1, defaultProperties, 10000).then(function(elements) {
				assert.deepEqual(elements, [ $target1 ], "Active call fulfilled.");
			});

			Velocity($target1, defaultProperties, 10000).then(function(elements) {
				assert.deepEqual(elements, [ $target1 ], "Queued call fulfilled.");
			});

			Velocity($target1, "stop", true).then(function(elements) {
				assert.deepEqual(elements, [ $target1 ], "Stop call fulfilled.");
			});	

			var $target2 = getTarget(),
				$target3 = getTarget();

			Velocity([ $target2, $target3 ], "fake", defaultOptions)["catch"](function(error) {
				assert.equal(error instanceof Error, true, "Invalid command caused promise rejection.");
			});

			Velocity([ $target2, $target3 ], defaultProperties, defaultOptions).then(function(elements) {
				assert.deepEqual(elements, [ $target2, $target3 ], "Elements passed back into resolved promise.");

				start();
			});		
		});

		/*****************************
		   Feature: Value Functions    
		*****************************/

		QUnit.test("Feature: Value Functions", function(assert) {
			var testWidth = 10;

			var $target1 = getTarget(),
				$target2 = getTarget();
			Velocity([ $target1, $target2 ], { width: function (i, total) { return (i + 1)/total * testWidth; } });

			assert.equal(Data($target1, pluginName).tweensContainer.width.endValue, parseFloat(testWidth) / 2, "Function value #1 passed to tween.");
			assert.equal(Data($target2, pluginName).tweensContainer.width.endValue, parseFloat(testWidth), "Function value #2 passed to tween.");
		});

		/***************************
		   Feature: Forcefeeding    
		***************************/

		QUnit.test("Feature: Forcefeeding", function(assert) {
			/* Note: Start values are always converted into pixels. W test the conversion ratio we already know to avoid additional work. */
			var testStartWidth = "1rem", testStartWidthToPx = "16px",
				testStartHeight = "10px";

			var $target = getTarget();
			Velocity($target, { width: [ 100, "linear", testStartWidth ], height: [ 100, testStartHeight ], opacity: [ defaultProperties.opacity, "easeInQuad" ]});

			assert.equal(Data($target, pluginName).tweensContainer.width.startValue, parseFloat(testStartWidthToPx), "Forcefed value #1 passed to tween.");
			assert.equal(Data($target, pluginName).tweensContainer.height.startValue, parseFloat(testStartHeight), "Forcefed value #2 passed to tween.");
			assert.equal(Data($target, pluginName).tweensContainer.opacity.startValue, defaultStyles.opacity, "Easing was misinterpreted as forcefed value.");
		});

		/*********************************
		   Feature: Colors (Shorthands)   
		*********************************/

		QUnit.test("Feature: Colors (Shorthands)", function(assert) {
			var $target = getTarget();
			Velocity($target, { borderColor: "#7871c2", color: [ "#297dad", "spring", "#5ead29" ] });

			assert.equal(Data($target, pluginName).tweensContainer.borderColorRed.endValue, 120, "Hex #1a component.");
			assert.equal(Data($target, pluginName).tweensContainer.borderColorGreen.endValue, 113, "Hex #1b component.");
			assert.equal(Data($target, pluginName).tweensContainer.borderColorBlue.endValue, 194, "Hex #1c component.");
			assert.equal(Data($target, pluginName).tweensContainer.colorRed.easing, "spring", "Per-property easing.");
			assert.equal(Data($target, pluginName).tweensContainer.colorRed.startValue, 94, "Forcefed hex #2a component.");
			assert.equal(Data($target, pluginName).tweensContainer.colorGreen.startValue, 173, "Forcefed hex #2b component.");
			assert.equal(Data($target, pluginName).tweensContainer.colorBlue.startValue, 41, "Forcefed hex #2c component.");
			assert.equal(Data($target, pluginName).tweensContainer.colorRed.endValue, 41, "Hex #3a component.");
			assert.equal(Data($target, pluginName).tweensContainer.colorGreen.endValue, 125, "Hex #3b component.");
			assert.equal(Data($target, pluginName).tweensContainer.colorBlue.endValue, 173, "Hex #3c component.");
		});

		/**********************************
		   Packaged Effect: slideUp/Down
		**********************************/

		QUnit.asyncTest("Packaged Effect: slideUp/Down", function(assert) {
			var $target1 = getTarget(),
				$target2 = getTarget();

			var initialStyles = {
				display: "none",
				paddingTop: "123px"
			};

			$target1.style.display = initialStyles.display;
			$target1.style.paddingTop = initialStyles.paddingTop;

			Velocity($target1, "slideDown", 
				{ 
					begin: function(elements) {
						assert.deepEqual(elements, [ $target1 ], "slideDown: Begin callback returned.");
					},
					complete: function(elements) {
						assert.deepEqual(elements, [ $target1 ], "slideDown: Complete callback returned.");
						assert.equal(Velocity.CSS.getPropertyValue($target1, "display"), Velocity.CSS.Values.getDisplayType($target1), "slideDown: display set to default.");
						notEqual(Velocity.CSS.getPropertyValue($target1, "height"), 0, "slideDown: height set.");
						assert.equal(Velocity.CSS.getPropertyValue($target1, "paddingTop"), initialStyles.paddingTop, "slideDown: paddingTop set.");
					}
				}
			).then(function(elements) {
				assert.deepEqual(elements, [ $target1 ], "slideDown: Promise fulfilled.");
			});

			Velocity($target2, "slideUp", 
				{ 
					begin: function(elements) {
						assert.deepEqual(elements, [ $target2 ], "slideUp: Begin callback returned.");
					},
					complete: function(elements) {
						assert.deepEqual(elements, [ $target2 ], "slideUp: Complete callback returned.");
						assert.equal(Velocity.CSS.getPropertyValue($target2, "display"), 0, "slideUp: display set to none.");
						notEqual(Velocity.CSS.getPropertyValue($target2, "height"), 0, "slideUp: height reset.");
						assert.equal(Velocity.CSS.getPropertyValue($target1, "paddingTop"), initialStyles.paddingTop, "slideUp: paddingTop reset.");
					}
				}
			).then(function(elements) {
				assert.deepEqual(elements, [ $target2 ], "slideUp: Promise fulfilled.");

				start();
			});
		});

		/***********************
		   UI Pack: Callbacks
		***********************/

		QUnit.asyncTest("UI Pack: Callbacks", function(assert) {
			expect(3);

			var $targets = [ getTarget(), getTarget() ];

			Velocity($targets, "transition.bounceIn", 
				{ 
					begin: function(elements) {
						assert.deepEqual(elements, $targets, "Begin callback returned.");
					},
					complete: function(elements) {
						assert.deepEqual(elements, $targets, "Complete callback returned.");
					}
				}
			).then(function(elements) {
				assert.deepEqual(elements, $targets, "Promise fulfilled.");

				start();
			});
		});

		/*********************
		   UI Pack: In/Out
		*********************/

		QUnit.asyncTest("UI Pack: In/Out", function(assert) {
			expect(8);

			var $target1 = getTarget();
			Velocity($target1, "transition.bounceIn", defaultOptions.duration);

			var $target2 = getTarget();
			Velocity($target2, "transition.bounceIn", { duration: defaultOptions.duration, display: "inline" });

			var $target3 = getTarget();
			Velocity($target3, "transition.bounceOut", defaultOptions.duration);

			var $target4 = getTarget();
			Velocity($target4, "transition.bounceOut", { duration: defaultOptions.duration, display: null });

			var $target5 = getTarget();
			$target5.style.visibility = "hidden";
			Velocity($target5, "transition.bounceIn", { duration: defaultOptions.duration, visibility: "visible" });

			var $target6 = getTarget();
			$target6.style.visibility = "visible";
			Velocity($target6, "transition.bounceOut", { duration: defaultOptions.duration, visibility: "hidden" });

			setTimeout(function() {
				notEqual(Velocity.CSS.getPropertyValue($target3, "display"), 0, "Out: display not prematurely set to none.");
				notEqual(Velocity.CSS.getPropertyValue($target6, "visibility"), "hidden", "Out: visibility not prematurely set to hidden.");
			}, asyncCheckDuration);

			setTimeout(function() {
				assert.equal(Velocity.CSS.getPropertyValue($target1, "display"), Velocity.CSS.Values.getDisplayType($target1), "In: display set to default.");
				assert.equal(Velocity.CSS.getPropertyValue($target2, "display"), "inline", "In: Custom inline value set.");

				assert.equal(Velocity.CSS.getPropertyValue($target3, "display"), 0, "Out: display set to none.");
				assert.equal(Velocity.CSS.getPropertyValue($target4, "display"), Velocity.CSS.Values.getDisplayType($target3), "Out: No display value set.");

				assert.equal(Velocity.CSS.getPropertyValue($target5, "visibility"), "visible", "In: visibility set to visible.");
				assert.equal(Velocity.CSS.getPropertyValue($target6, "visibility"), "hidden", "Out: visibility set to hidden.");

				start();
			}, completeCheckDuration);
		});

		/**************************
		   UI Pack: Call Options
		**************************/

		QUnit.asyncTest("UI Pack: Call Options", function(assert) {
			expect(7);

			var UICallOptions1 = {
					delay: 123,
					duration: defaultOptions.duration,
					loop: true, // Should get ignored
					easing: "spring" // Should get ignored
				};

			var $target1 = getTarget();
			Velocity($target1, "transition.slideLeftIn", UICallOptions1);

			setTimeout(function() {
				// Note: We can do this because transition.slideLeftIn is composed of a single call.
				assert.equal(Data($target1, pluginName).opts.delay, UICallOptions1.delay, "Whitelisted option passed in.");
				notEqual(Data($target1, pluginName).opts.loop, UICallOptions1.loop, "Non-whitelisted option not passed in #1a.");
				notEqual(Data($target1, pluginName).opts.easing, UICallOptions1.easing, "Non-whitelisted option not passed in #1a.");
				assert.equal(!/velocity-animating/.test(Data($target1, pluginName).className), true, "Duration option passed in.");
			}, completeCheckDuration);

			var UICallOptions2 = {
					stagger: 100,
					duration: defaultOptions.duration,
					backwards: true
				};

			var $targets = [ getTarget(), getTarget(), getTarget() ];
			Velocity($targets, "transition.slideLeftIn", UICallOptions2);

			setTimeout(function() {
				assert.equal(Data($targets[0], pluginName).opts.delay, UICallOptions2.stagger * 2, "Backwards stagger delay passed in #1a.");
				assert.equal(Data($targets[1], pluginName).opts.delay, UICallOptions2.stagger * 1, "Backwards stagger delay passed in #1b.");
				assert.equal(Data($targets[2], pluginName).opts.delay, UICallOptions2.stagger * 0, "Backwards stagger delay passed in #1c.");

				start();
			}, completeCheckDuration);
		});

		/****************************
		   UI Pack: RegisterEffect
		****************************/

		QUnit.asyncTest("UI Pack: RegisterEffect", function(assert) {
			expect(2);

			var effectDefaultDuration = 800;
			Velocity.RegisterUI("callout.twirl", {
			    defaultDuration: effectDefaultDuration,
			    calls: [ 
					[ { rotateZ: 1080 }, 0.50 ],
					[ { scaleX: 0.5 }, 0.25, { easing: "spring" } ],
					[ { scaleX: 1 }, 0.25, { easing: "spring" } ]
			    ]
			});

			var $target1 = getTarget();
			Velocity($target1, "callout.twirl");

			setTimeout(function() {
				assert.equal(parseFloat(Velocity.CSS.getPropertyValue($target1, "rotateZ")), 1080, "First call's property animated.");
				assert.equal(parseFloat(Velocity.CSS.getPropertyValue($target1, "scaleX")), 1, "Last call's property animated.");

				start();
			}, effectDefaultDuration * 1.50);
		});

		/*************************
		   UI Pack: RunSequence
		*************************/

		QUnit.asyncTest("UI Pack: RunSequence", function(assert) {
			expect(3);

			var $target1 = getTarget(),
				$target2 = getTarget(),
				$target3 = getTarget();

			var mySequence = [
					{ elements: $target1, properties: { opacity: defaultProperties.opacity } },
					{ elements: $target2, properties: { height: defaultProperties.height } },
					{ elements: $target3, properties: { width: defaultProperties.width }, options: { 
							delay: 100,
							sequenceQueue: false,
							complete: function() {
								assert.equal(parseFloat(Velocity.CSS.getPropertyValue($target1, "opacity")), defaultProperties.opacity, "First call's property animated.");
								assert.equal(parseFloat(Velocity.CSS.getPropertyValue($target2, "height")), defaultProperties.height, "Second call's property animated.");
								assert.equal(parseFloat(Velocity.CSS.getPropertyValue($target3, "width")), defaultProperties.width, "Last call's property animated.");

								start();
							}
						}
					}
				];

			Velocity.RunSequence(mySequence);
		});

		/*********************
		   Command: Scroll
		*********************/

		if ($) {
			/* Window scrolling. */
			QUnit.asyncTest("Command: Scroll (Window)", function(assert) {
				var $details = $("#details"),
					$scrollTarget1 = $("<div>Scroll target #1. Should stop 50 pixels above this point.</div>"),
					$scrollTarget2 = $("<div>Scroll target #2. Should stop 50 pixels before this point.</div>"),
					scrollOffset = -50;

				$scrollTarget1
					.css({ position: "relative", top: 3000, height: 100, paddingBottom: 10000 })
					.appendTo($("body"));

				$scrollTarget2
					.css({ position: "absolute", top: 100, left: 3000, width: 100, paddingRight: 15000 })
					.appendTo($("body"));

				$scrollTarget1
					.velocity("scroll", { duration: 500, offset: scrollOffset, complete: function() {
							assert.equal(Math.abs(Velocity.State.scrollAnchor[Velocity.State.scrollPropertyTop] - ($scrollTarget1.offset().top + scrollOffset)) <= 100, true, "Page scrolled top with a scroll offset.");
						}
					})
					.velocity({ opacity: 0.5 }, function() {
					    $details
						    .velocity({ opacity: 0.5 }, 500)
						    .velocity("scroll", 500)
						    .velocity({ opacity: 1 }, 500, function() {
						    	//alert(Velocity.State.scrollAnchor[Velocity.State.scrollPropertyTop] + " " + ($details.offset().top + scrollOffset))
								assert.equal(Math.abs(Velocity.State.scrollAnchor[Velocity.State.scrollPropertyTop] - ($details.offset().top + scrollOffset)) <= 100, true, "Page scroll top was chained.");

								//$scrollTarget1.remove();

								$scrollTarget2
									.velocity("scroll", { duration: 500, axis: "x", offset: scrollOffset, complete: function() {
											/* Phones can reposition the browser's scroll position by a 10 pixels or so, so we just check for a value that's within that range. */
											assert.equal(Math.abs(Velocity.State.scrollAnchor[Velocity.State.scrollPropertyLeft] - ($scrollTarget2.offset().left + scrollOffset)) <= 100, true, "Page scrolled left with a scroll offset.");
										}
									})
									.velocity({ opacity: 0.5 }, function() {
									    $details
										    .velocity({ opacity: 0.5 }, 500)
										    .velocity("scroll", { duration: 500, axis: "x" })
										    .velocity({ opacity: 1 }, 500, function() {
												assert.equal(Math.abs(Velocity.State.scrollAnchor[Velocity.State.scrollPropertyLeft] - ($details.offset().left + scrollOffset)) <= 100, true, "Page scroll left was chained.");
												start();
										    });
									});
						    });
					});
			});

			/* Element scrolling. */
			QUnit.asyncTest("Command: Scroll (Element)", function(assert) {
				expect(2);

				var $scrollTarget1 = $("\
					<div id='scroller'>\
						aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
						aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
						aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
						aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
						aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
						<div id='scrollerChild1'>\
							Stop #1\
							bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\
							bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\
							bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\
							bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\
							bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\
						</div>\
						cccccccccccccccccccccccccccccccccccccccccccccccccccccccc\
						cccccccccccccccccccccccccccccccccccccccccccccccccccccccc\
						cccccccccccccccccccccccccccccccccccccccccccccccccccccccc\
						cccccccccccccccccccccccccccccccccccccccccccccccccccccccc\
						cccccccccccccccccccccccccccccccccccccccccccccccccccccccc\
						<div id='scrollerChild2'>\
							Stop #2\
							dddddddddddddddddddddddddddddddddddddddddddddddddddddddd\
							dddddddddddddddddddddddddddddddddddddddddddddddddddddddd\
							dddddddddddddddddddddddddddddddddddddddddddddddddddddddd\
							dddddddddddddddddddddddddddddddddddddddddddddddddddddddd\
							dddddddddddddddddddddddddddddddddddddddddddddddddddddddd\
						</div>\
						eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\
						eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\
						eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\
						eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\
						eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\
					</div>\
				");

				$scrollTarget1
					.css({ position: "absolute", backgroundColor: "white", top: 100, left: "50%", width: 500, height: 100, overflowY: "scroll" })
					.appendTo($("body"));

				/* Test with a jQuery object container. */
				$("#scrollerChild1").velocity("scroll", { container: $("#scroller"), duration: 750, complete: function() {
						/* Test with a raw DOM element container. */
						$("#scrollerChild2").velocity("scroll", { container: $("#scroller")[0], duration: 750, complete: function() {
								/* This test is purely visual. */
								ok(true);

								$scrollTarget1.remove();

								var $scrollTarget2 = $("\
									<div id='scroller'>\
										<div id='scrollerChild1' style='float: left; width: 20%;'>\
											Stop #1\
											bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\
											bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\
											bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\
											bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\
											bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\
											cccccccccccccccccccccccccccccccccccccccccccccccccccccccc\
											cccccccccccccccccccccccccccccccccccccccccccccccccccccccc\
											cccccccccccccccccccccccccccccccccccccccccccccccccccccccc\
											cccccccccccccccccccccccccccccccccccccccccccccccccccccccc\
											cccccccccccccccccccccccccccccccccccccccccccccccccccccccc\
										</div>\
										<div id='scrollerChild2' style='float: right; width: 20%;'>\
											Stop #2\
											dddddddddddddddddddddddddddddddddddddddddddddddddddddddd\
											dddddddddddddddddddddddddddddddddddddddddddddddddddddddd\
											dddddddddddddddddddddddddddddddddddddddddddddddddddddddd\
											dddddddddddddddddddddddddddddddddddddddddddddddddddddddd\
											dddddddddddddddddddddddddddddddddddddddddddddddddddddddd\
											eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\
											eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\
											eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\
											eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\
											eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\
										</div>\
									</div>\
								");

								$scrollTarget2
									.css({ position: "absolute", backgroundColor: "white", top: 100, left: "50%", width: 100, height: 500, overflowX: "scroll" })
									.appendTo($("body"));

								/* Test with a jQuery object container. */
								$("#scrollerChild2").velocity("scroll", { axis: "x", container: $("#scroller"), duration: 750, complete: function() {
										/* Test with a raw DOM element container. */
										$("#scrollerChild1").velocity("scroll", { axis: "x", container: $("#scroller")[0], duration: 750, complete: function() {
												/* This test is purely visual. */
												ok(true);

												$scrollTarget2.remove();

												start();
											}
										});
									}
								});
							}
						});
					}
				});
			});
		}
		</script>
	</body>
</html>